package com.zl.test.activiti.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.DeploymentQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import com.github.pagehelper.PageInfo;
import com.github.pagehelper.util.StringUtil;
import com.zl.test.activiti.ActivitiService;

/**
 * activiti流程部署,流程定义,流程实例,历史实例
 * @author zhangle
 */
@Service
public class ActivitiServiceImpl implements ActivitiService {

	private static final Logger logger = LogManager.getLogger(ActivitiServiceImpl.class.getName());

	@Autowired private RepositoryService repositoryService;
	@Autowired private RuntimeService runtimeService;
	@Autowired private TaskService taskService;
	@Autowired private HistoryService historyService;
	
	
	//================ 流程部署 ======================
	/**
	 * 查询流程部署
	 * @return
	 */
	public PageInfo<Map<String,Object>> queryDeployments(String pdkey,int pageNum,int pageSize) {
		
		DeploymentQuery query = repositoryService.createDeploymentQuery();
    	if(StringUtil.isNotEmpty(pdkey)) {
    		query.processDefinitionKey(pdkey);
    	}
    	
		long count=query.count();
    	List<Deployment> deploys=query.orderByDeploymentName()
    			.orderByDeploymenTime()
    			.desc()
    			.listPage((pageNum-1)*pageSize, pageSize);
    	
    	PageInfo<Map<String,Object>> pageinfo=new PageInfo<>();
    	pageinfo.setPageNum(pageNum);
    	pageinfo.setPageSize(pageSize);
    	pageinfo.setTotal(count);
    	pageinfo.setList(toDeployments(deploys));
    	
		return pageinfo;
	}
    
    private List<Map<String,Object>> toDeployments(List<Deployment> deploys) {
    	
    	List<Map<String,Object>> datalist=new ArrayList<Map<String,Object>>();
    	for(Deployment deploy:deploys) {
    		Map<String,Object> data=new HashMap<String, Object>();
    		data.put("id", deploy.getId());
    		data.put("name", deploy.getName());
    		data.put("category", deploy.getCategory());
    		data.put("deploymentTime", deploy.getDeploymentTime());
    		data.put("tenantId", deploy.getTenantId());
    		datalist.add(data);
    	}
    	return datalist;
    }
	
    /**
     * 删除流程部署
     * @param deployId
     */
	public void delDeployment(String deployId) {
		repositoryService.deleteDeployment(deployId);
	}
	
	/**
	 * 部署zip流程
	 */
	public void deployZip(String name,String category,ZipInputStream zipStream) {
		
		try {
			DeploymentBuilder deploy = repositoryService.createDeployment().name(name).category(category);
			deploy.addZipInputStream(zipStream).deploy();
		} catch (Exception e) {
			logger.error("部署流程定义失败",e);
		} finally {
			IOUtils.closeQuietly(zipStream);
		}
	}
	
	/**
	 * 部署bpmn流程
	 */
	public void deployBpmn(String name,String category,InputStream bpmnStream,String resourceName) {
		
		try {
			DeploymentBuilder deploy = repositoryService.createDeployment().name(name).category(category);
			deploy.addInputStream(resourceName, bpmnStream).deploy();
		} catch (Exception e) {
			logger.error("部署流程定义失败",e);
		} finally {
			IOUtils.closeQuietly(bpmnStream);
		}
	}
	
	//================ 流程定义 ======================
	/**
	 * 查询流程定义
	 * @return
	 */
	public PageInfo<Map<String,Object>> queryDefinitions(String pdkey,int pageNum,int pageSize) {
		
		ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
    	if(StringUtil.isNotEmpty(pdkey)) {
    		query.processDefinitionKey(pdkey);
    	}
    	
    	long count=query.count();
    	List<ProcessDefinition> defines=query.orderByProcessDefinitionName()
				.orderByProcessDefinitionVersion().asc()
				.listPage((pageNum-1)*pageSize, pageSize);
    	
    	PageInfo<Map<String,Object>> pageinfo=new PageInfo<>();
    	pageinfo.setPageNum(pageNum);
    	pageinfo.setPageSize(pageSize);
    	pageinfo.setTotal(count);
    	pageinfo.setList(toDefinitions(defines));
    	
		return pageinfo;
	}
    
    private List<Map<String,Object>> toDefinitions(List<ProcessDefinition> defines) {
    	
    	List<Map<String,Object>> datalist=new ArrayList<Map<String,Object>>();
    	for(ProcessDefinition defin:defines) {
    		Map<String,Object> data=new HashMap<String, Object>();
    		data.put("id", defin.getId());
    		data.put("name", defin.getName());
    		data.put("key", defin.getKey());
    		data.put("version", defin.getVersion());
    		data.put("category", defin.getCategory());
    		data.put("isSuspended", defin.isSuspended());
    		data.put("description", defin.getDescription());
    		data.put("tenantId", defin.getTenantId());
    		data.put("deploymentId", defin.getDeploymentId());
    		data.put("diagramResourceName", defin.getDiagramResourceName());
    		data.put("resourceName", defin.getResourceName());
    		datalist.add(data);
    	}
    	return datalist;
    }
	
    /**
     * 挂起或激活流程定义
     * @param defineid
     * @param suspend	是否挂起流程 true:是,false:否
     */
	public void suspendDefinition(String defineid,boolean suspend) {
    	if(suspend) {
    		repositoryService.suspendProcessDefinitionById(defineid);
    	} else {
    		repositoryService.activateProcessDefinitionById(defineid);
    	}
	}
	
	/**
	 * 获取流程图文件流
	 * @param defineid
	 * @return
	 */
    public OutputStream processDiagram(String defineid) {
		
    	OutputStream os=null;
		InputStream is=null;
		try {
			os=new ByteArrayOutputStream();
			is=repositoryService.getProcessDiagram(defineid);
			IOUtils.copy(is, os);
			
			return os;
		} catch (IOException e) {
			logger.error(e);
			throw new RuntimeException("下载流程图失败");
		} finally {
			IOUtils.closeQuietly(is);
			IOUtils.closeQuietly(os);
		}
    }
    
    //================ 流程实例 ======================
    
    /**
	 * 查询流程实例
	 * @return
	 */
	public PageInfo<Map<String,Object>> queryInstance(String pdkey,int pageNum,int pageSize) {
		
    	ProcessInstanceQuery query=runtimeService.createProcessInstanceQuery();
    	if(StringUtil.isNotEmpty(pdkey)) {
    		query.processDefinitionKey(pdkey);
    	}
    	
    	long count=query.count();
    	List<ProcessInstance> instances=query.orderByProcessDefinitionKey()
    			.desc()
    			.listPage((pageNum-1)*pageSize, pageSize);
    	
    	PageInfo<Map<String,Object>> pageinfo=new PageInfo<>();
    	pageinfo.setPageNum(pageNum);
    	pageinfo.setPageSize(pageSize);
    	pageinfo.setTotal(count);
    	pageinfo.setList(toInstances(instances));
    	
		return pageinfo;
	}
    
    private List<Map<String,Object>> toInstances(List<ProcessInstance> instances) {
    	
    	List<Map<String,Object>> datalist=new ArrayList<Map<String,Object>>();
    	for(ProcessInstance pi:instances) {
    		datalist.add(toInstance(pi));
    	}
    	return datalist;
    }
    
    private Map<String,Object> toInstance(ProcessInstance pi) {
    	
    	Map<String,Object> instmap=new HashMap<String, Object>();
		instmap.put("id", pi.getId());
		instmap.put("name", pi.getName());
		instmap.put("activityId", pi.getActivityId());
		instmap.put("processDefinitionId", pi.getProcessDefinitionId());
		instmap.put("processDefinitionName", pi.getProcessDefinitionName());
		instmap.put("processDefinitionKey", pi.getProcessDefinitionKey());
		instmap.put("processDefinitionVersion", pi.getProcessDefinitionVersion());
		instmap.put("deploymentId", pi.getDeploymentId());
		instmap.put("businessKey", pi.getBusinessKey());
		instmap.put("tenantId", pi.getTenantId());
		instmap.put("isSuspended", pi.isSuspended());
		
		return instmap;
    }
	
    /**
     * 获取流程实例
     * @param pid
     * @return
     */
    public Map<String,Object> getInstance(String pid) {
    	
		ProcessInstance pi=runtimeService.createProcessInstanceQuery().processInstanceId(pid).singleResult();
		Map<String, Object> vars=runtimeService.getVariables(pi.getId());
		
		Map<String,Object> instmap=toInstance(pi);
		instmap.putAll(vars);
		return instmap;
    }
    
    /**
     * 查询流程任务
     * @param pid
     * @return
     */
    public List<Map<String,Object>> queryTasks(String pid) {
    	
    	List<Task> tasks=taskService.createTaskQuery().processInstanceId(pid)
    			.orderByTaskCreateTime().asc().list();
		
    	List<Map<String,Object>> tasklist=new ArrayList<>();
    	for(Task task:tasks) {
    		tasklist.add(toTask(task));
    	}
    	
		return tasklist;
    }
    
    private Map<String,Object> toTask(Task task) {
    	
    	Map<String,Object> taskmap=new HashMap<String, Object>();
		taskmap.put("id", task.getId());
		taskmap.put("name", task.getName());
		taskmap.put("assignee", task.getAssignee());
		taskmap.put("category", task.getCategory());
		taskmap.put("createTime", task.getCreateTime());
		taskmap.put("desc", task.getDescription());
		taskmap.put("dueDate", task.getDueDate());
		taskmap.put("processDefinitionId", task.getProcessDefinitionId());
		taskmap.put("processInstanceId", task.getProcessInstanceId());
		taskmap.put("taskDefinitionKey", task.getTaskDefinitionKey());
		
		return taskmap;
    }
    
    /**
     * 挂起或激活流程实例
     * @param pid
     * @return
     */
	public void suspendInstance(String pid,boolean suspend) {
		
		if(suspend) {
			runtimeService.suspendProcessInstanceById(pid);
    	} else {
    		runtimeService.activateProcessInstanceById(pid);
    	}
	}

	/**
	 * 删除流程实例
	 * @param pid
	 * @return
	 */
	public void delete(String pid,String deleteReason) {
		runtimeService.deleteProcessInstance(pid, "admin delete");
	}
	
    //================ 流程历史 ======================
	
	/**
	 * 查询流程实例
	 * @return
	 */
	public PageInfo<Map<String,Object>> queryHistoryInstance(String pdkey,int pageNum,int pageSize) {
		
    	HistoricProcessInstanceQuery query=historyService.createHistoricProcessInstanceQuery();
    	if(StringUtil.isNotEmpty(pdkey)) {
    		query.processDefinitionKey(pdkey);
    	}
    	
    	long count=query.count();
    	List<HistoricProcessInstance> instances=query.orderByProcessInstanceEndTime()
    			.desc()
    			.listPage((pageNum-1)*pageSize, pageSize);
    	    	
    	PageInfo<Map<String,Object>> pageinfo=new PageInfo<>();
    	pageinfo.setPageNum(pageNum);
    	pageinfo.setPageSize(pageSize);
    	pageinfo.setTotal(count);
    	pageinfo.setList(toHistoryInstance(instances));
    	
		return pageinfo;
	}
    
    private List<Map<String,Object>> toHistoryInstance(List<HistoricProcessInstance> instances) {
    	
    	List<Map<String,Object>> datalist=new ArrayList<Map<String,Object>>();
    	for(HistoricProcessInstance inst:instances) {
    		datalist.add(toHistoryInstance(inst));
    	}
    	return datalist;
    }
    
    private Map<String,Object> toHistoryInstance(HistoricProcessInstance inst) {
    	
    	Map<String,Object> data=new HashMap<String, Object>();
		data.put("id", inst.getId());
		data.put("name", inst.getName());
		data.put("processDefinitionId", inst.getProcessDefinitionId());
		data.put("businessKey", inst.getBusinessKey());
		data.put("tenantId", inst.getTenantId());
		data.put("durationInMillis", inst.getDurationInMillis());
		data.put("endTime", inst.getEndTime());
		data.put("startActivityId", inst.getStartActivityId());
		data.put("startTime", inst.getStartTime());
		data.put("startUserId", inst.getStartUserId());
		data.put("superProcessInstanceId", inst.getSuperProcessInstanceId());
		
		return data;
    }
	
    /**
     * 获取历史流程实例
     * @param pid
     * @return
     */
    public Map<String,Object> getHistoryInstance(String pid) {
    	HistoricProcessInstance pi=historyService.createHistoricProcessInstanceQuery().processInstanceId(pid).singleResult();
		Map<String,Object> instmap=toHistoryInstance(pi);
		return instmap;
    }
    
    /**
     * 查询历史任务
     * @param pid
     * @return
     */
    public List<Map<String,Object>> queryHistoryTask(String pid) {
    	
    	List<HistoricTaskInstance> tasks=historyService.createHistoricTaskInstanceQuery()
    			.processInstanceId(pid).orderByTaskCreateTime().asc().list();
		
    	List<Map<String,Object>> tasklist=new ArrayList<>();
		for(HistoricTaskInstance task:tasks) {
			tasklist.add(toHistoryTask(task));
		}
		
		return tasklist;
    }
    
    private Map<String,Object> toHistoryTask(HistoricTaskInstance task) {
    	
    	Map<String,Object> taskmap=new HashMap<String, Object>();
		taskmap.put("id", task.getId());
		taskmap.put("name", task.getName());
		taskmap.put("assignee", task.getAssignee());
		taskmap.put("category", task.getCategory());
		taskmap.put("createTime", task.getCreateTime());
		taskmap.put("desc", task.getDescription());
		taskmap.put("dueDate", task.getDueDate());
		taskmap.put("processDefinitionId", task.getProcessDefinitionId());
		taskmap.put("processInstanceId", task.getProcessInstanceId());
		taskmap.put("taskDefinitionKey", task.getTaskDefinitionKey());
		
		return taskmap;
    }
    
	/**
	 * 删除历史流程实例
	 * @param pid
	 * @return
	 */
	public void deleteHistoryInstance(@PathVariable String pid) {
		historyService.deleteHistoricProcessInstance(pid);
	}
	
	//================ 流程处理 ======================
	
}
